﻿import { _decorator, Component, Node, AudioClip } from 'cc';
import { SysBase } from '../base/SysBase';
import { DestroyCB } from '../common/DestroyCB';
import { Functor } from '../common/Functor';
import { IntervalMgr } from '../common/IntervalMgr';
import { Misc } from '../common/Misc';
import { SyncSys } from './SyncSys';
const { ccclass, property } = _decorator;

@ccclass('TimeSys')
export class TimeSys extends SysBase {
    timeMap = new Map<number, Map<any, Array<any>>>();
    nowTime = 0;
    timeInterval = 1;
    keyInterval = 100;
    static OBJ_DELETE_SET_KEY = "_obj_delete_set_key";
    destroyObj = new Set<any>();
    isEndTimer = true;

    loopObjSet = new Set<any>();
    removeLoopObjSet = new Set<any>();
    onLoad() {
        this.schedule(this.loop, this.timeInterval / this.keyInterval);
    }

    async loop() {
        if (!this.isEndTimer)
            return;
        this.isEndTimer = false
        this.nowTime += this.timeInterval;

        IntervalMgr.loop(this.timeInterval);

        await this.runTimer();
        await this.runLoop(this.timeInterval);
        this.isEndTimer = true
    }

    public async runLoop(dt) {
        let loopObjSet = new Set();
        for (let obj of this.loopObjSet) {
            if (this.removeLoopObjSet.has(obj))
                continue;

            await Functor.getFunc(obj, "loop")(dt);
            loopObjSet.add(obj)
        }
        this.loopObjSet = loopObjSet;
    }


    public addLoop(obj: any) {
        this.loopObjSet.add(obj)
    }

    public removeLoop(obj: any) {
        this.removeLoopObjSet.add(obj)
    }

    getNowTime() {
        return this.nowTime;
    }

    public addTimer(obj: any, funcName: string, dt: number, isLoop = false, ...args: any) {
        let callTime = this.nowTime + Math.round(dt * this.keyInterval);
        let objMap = this.timeMap.get(callTime);
        if (objMap == null) {
            objMap = new Map<any, Array<string>>()
            this.timeMap.set(callTime, objMap);
        }
        let funcObjArr = objMap.get(obj);
        if (funcObjArr == null) {
            funcObjArr = new Array<string>();
            objMap.set(obj, funcObjArr);
            DestroyCB.addCb(obj, this, "onObjDestroy");
        }
        funcObjArr.push({
            isLoop: isLoop,
            funcName: funcName,
            dt: dt,
            args: args
        });
        let deleteSet = obj[TimeSys.OBJ_DELETE_SET_KEY];
        if (deleteSet == null) {
            obj[TimeSys.OBJ_DELETE_SET_KEY] = new Set<string>();
        }

        obj[TimeSys.OBJ_DELETE_SET_KEY].add(callTime);
    }

    removeTimer(obj: any, funcName: string) {
        let deleteSet = obj[TimeSys.OBJ_DELETE_SET_KEY];
        if (deleteSet == null)
            return;
        for (let callTime of deleteSet) {
            let objMap = this.timeMap.get(callTime);
            if (objMap == null)
                continue;
            let funcObjArr = objMap.get(obj);
            if (funcObjArr == null)
                continue;
            let index = -1;
            for (let i in funcObjArr) {
                if (funcObjArr[i].funcName == funcName) {
                    funcObjArr[i]["isDelete"] = true;
                    index = Number(i);
                    break;
                }
            }
            if (index > -1) {
                funcObjArr.splice(index, 1)
            }
        }
    }

    onObjDestroy(watchDestroyObj: any) {
        this.destroyObj.add(watchDestroyObj);
        let deleteSet = watchDestroyObj[TimeSys.OBJ_DELETE_SET_KEY];
        if (deleteSet == null)
            return;

        for (let callTime of deleteSet) {
            let objMap = this.timeMap.get(callTime);
            if (objMap == null)
                continue;
            objMap.delete(watchDestroyObj);
        }
    }

    async runTimer() {
        let nowTime = this.nowTime
        let objMap = this.timeMap.get(nowTime);
        if (objMap == null) {
            return;
        }
        let lKeys = objMap.keys()
        for (let obj of lKeys) {
            let funcObjArr = objMap.get(obj)
            if (funcObjArr == null)
                continue;
            funcObjArr = Misc.deepCopyArray(funcObjArr)
            for (let i = 0, len = funcObjArr.length; i < len; i++) {
                let funcObj = funcObjArr[i]
                await this.runFunc(obj, funcObj);
            }
        }

        this.timeMap.delete(nowTime);
    }

    async runFunc(obj, funcObj) {

        await Functor.getFunc(obj, funcObj.funcName, ...funcObj.args)();
        if (this.destroyObj.has(obj)) {
            return;
        }
        if (obj[TimeSys.OBJ_DELETE_SET_KEY] == null) {
            return;
        }

        if (funcObj["isDelete"])
            return;

        if (funcObj.isLoop) {
            obj[TimeSys.OBJ_DELETE_SET_KEY].delete(this.nowTime);
            this.addTimer(obj, funcObj.funcName, funcObj.dt, funcObj.isLoop, ...funcObj.args);
        }
    }
}

